﻿#region

using System.Globalization;

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;

using Leo.Core.AutoReponseWarpper;
using Leo.Core.Identity;
using Leo.Core.Jwt;
using Leo.Core.Swagger;

using Serilog;

using Volo.Abp;
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
using Volo.Abp.AspNetCore.Mvc.Json;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.BlobStoring;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.RabbitMQ;

#endregion

namespace Leo.Core.Shared.Module;

[DependsOn(
    typeof(AbpAutofacModule),
    typeof(AbpAspNetCoreMultiTenancyModule),
    typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
    typeof(AbpAspNetCoreSerilogModule),
    typeof(AbpEventBusRabbitMqModule),
    typeof(AbpCachingModule),
    typeof(AbpCachingStackExchangeRedisModule),
    typeof(BlobModule),
    typeof(MultiTenantModule),
    typeof(SwaggerModule),
    typeof(AutoWrapperModule)
)]
public class SharedHostApiModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var configuration = context.Services.GetConfiguration();
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        context.Services.AddLogging(builder => builder.AddConsole().AddDebug());
        ConfigureCache(hostingEnvironment.ApplicationName, configuration);
        //配置JWT服务
        context.ConfigureJwtTokenService();
        //配置Abp.vnext身份认证模块
        context.SetupIdentity(context.ReadIsConfigIdentity());
        //ConfigureConventionalControllers(); //单独实现
        //ConfigureVirtualFileSystem(context);//单独实现
        // 配置认证
        ConfigureAuthentication(context);
        //配置MVC默认JSON序列化方案为Json.Net
        ConfigureMVCNewtonsoftJson(context);
        // 配置ABP异常拦截
        //ConfigureABPException(context);
        //配置国际化
        ConfigureLocalization();
        ConfigureCors(context, configuration);
        //配置CSRF
        ConfigureAbpAntiForgeryOptions(context, ignoredHttpMethods: new List<string> { "POST", "PUT", "DELETE" });

        //TODO
        Configure<IdentityOptions>(options =>
        {
            options.Password.RequireDigit = false;
            options.Password.RequireLowercase = false;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = false;
            options.Password.RequiredLength = 6;
        });

        Configure<AbpRabbitMqOptions>(options =>
        {
            Console.WriteLine("AbpRabbitMqOptions HostName = " + options.Connections.Default.HostName);
            Console.WriteLine("AbpRabbitMqOptions HostName = " + options.Connections.Default.Port);
            Console.WriteLine("AbpRabbitMqOptions VirtualHost = " + options.Connections.Default.VirtualHost);
            Console.WriteLine("AbpRabbitMqOptions UserName = " + options.Connections.Default.UserName);
            Console.WriteLine("AbpRabbitMqOptions AutomaticRecoveryEnabled = " + options.Connections.Default.AutomaticRecoveryEnabled);
            //options.Connections.Default.UserName = "user";
            //options.Connections.Default.Password = "pass";
            //options.Connections.Default.HostName = "123.123.123.123";
            //options.Connections.Default.Port = 5672;
        });

        Configure<AbpRabbitMqEventBusOptions>(options =>
        {
            Console.WriteLine("AbpRabbitMqEventBusOptions ClientName = " + options.ClientName);
            Console.WriteLine("AbpRabbitMqEventBusOptions ExchangeName = " + options.ExchangeName);
            //options.ClientName = "TestApp1";
            //options.ExchangeName = "TestMessages";
        });

        //context.Services.AddMiniProfiler(options =>
        //{
        //    options.RouteBasePath = "/profiler"; //访问页面：/profiler/results
        //    options.EnableMvcFilterProfiling = false;
        //    options.EnableMvcViewProfiling = false;
        //}).AddEntityFramework();

        ConfigureHealthChecks(context);
    }

    /// <summary>
    /// 配置MVC默认JSON序列化方案为Json.Net
    /// </summary>
    /// <param name="context"></param>
    public static void ConfigureMVCNewtonsoftJson(ServiceConfigurationContext context)
    {
        //context.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcNewtonsoftJsonOptions>, AbpMvcNewtonsoftJsonOptionsSetup>());
        //context.Services.AddMvc().AddNewtonsoftJson(options =>
        //{
        //    options.SerializerSettings.Converters.
        //           Add(new Newtonsoft.Json.Converters.StringEnumConverter());
        //});
    }

    /// <summary>
    /// 配置CSRF
    /// </summary>
    /// <param name="context"></param>
    public static void ConfigureAbpAntiForgeryOptions(ServiceConfigurationContext context, List<string> ignoredHttpMethods, bool autoValidate = false)
    {
        context.Services.Configure<AbpAntiForgeryOptions>(options =>
        {
            options.AutoValidate = autoValidate;
            foreach (var validateIgnoredHttpMethod in ignoredHttpMethods)
            {
                options.AutoValidateIgnoredHttpMethods.Add(validateIgnoredHttpMethod);
            }
        });
    }


    /// <summary>
    /// 配置认证
    /// </summary>
    /// <param name="context"></param>
    /// <param name="configuration"></param>
    public void ConfigureAuthentication(ServiceConfigurationContext context)
    {
        var configuration = context.Services.GetConfiguration();
        var jwtOption = new JwtOption();
        configuration.Bind(JwtOption.SectionName, jwtOption);
        if (jwtOption.SecurityKey == null) return;
        context.Services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                SaveSigninToken = true,//保存token,后台验证token是否生效(重要)
                ValidateIssuer = true,//是否验证Issuer
                ValidateAudience = true,//是否验证Audience
                ValidateLifetime = true,//是否验证失效时间
                ValidateIssuerSigningKey = true,//是否验证SecurityKey
                ValidAudience = jwtOption.Audience,//Audience
                ValidIssuer = jwtOption.Issuer,//Issuer，这两项和前面签发jwt的设置一致
                IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(jwtOption.SecurityKey))
            };
            options.Events = new JwtBearerEvents
            {
                OnAuthenticationFailed = context =>
                {
                    if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                    {
                        context.Response.Headers.Add("act", "expired");
                    }
                    return Task.CompletedTask;
                }
            };
        });
    }


    private void ConfigureLocalization()
    {
        Configure<AbpLocalizationOptions>(options =>
        {
            options.Languages.Add(new LanguageInfo("en", "en", "English"));
            options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
            options.Languages.Add(new LanguageInfo("zh-Hant", "zh-Hant", "繁體中文"));
        });
        Configure<RequestLocalizationOptions>(options =>
        {
            var supportedCultures = new List<CultureInfo> { new("en"), new("zh-Hans"), new("zh-Hant") };

            options.DefaultRequestCulture = new RequestCulture("zh-Hans");
            options.SupportedCultures = supportedCultures;
            options.SupportedUICultures = supportedCultures;
        });
    }

    private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
    {
        var corsValue = configuration.GetValue<string>("App:CorsOrigins", "*");
        Console.WriteLine($"App:CorsOrigins = {configuration["App:CorsOrigins"]}");
        Console.WriteLine($"App:CorsOrigins = {corsValue}");
        var corsOrigins = configuration["App:CorsOrigins"];
        if (corsOrigins == null) return;
        context.Services.AddCors(options =>
        {
            options.AddDefaultPolicy(builder =>
            {
                builder
                    .WithOrigins(
                        corsOrigins
                            .Split(",", StringSplitOptions.RemoveEmptyEntries)
                            .Select(o => o.RemovePostFix("/"))
                            .ToArray()
                    )
                    .WithAbpExposedHeaders()
                    .SetIsOriginAllowedToAllowWildcardSubdomains()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            });
        });
    }

    /// <summary>
    /// 配置ABP 异常拦截器
    /// </summary>
    /// <param name="context"></param>
    public void ConfigureABPException(ServiceConfigurationContext context)
    {
        context.Services.AddMvc(options =>
        {
            //options.Filters.ReplaceOne(x => (x as ServiceFilterAttribute)?.ServiceType?.Name == nameof(AbpExceptionFilter)
            //, new ServiceFilterAttribute(typeof(ExceptionFilter)));
        });
    }

    private void ConfigureCache(string appName, IConfiguration configuration)
    {
        try
        {
            if (string.IsNullOrEmpty(appName))
            {
                appName = "N6Bsjc";
            }
            var keyPrefix = configuration["App:KeyPrefix"];
            if (string.IsNullOrEmpty(keyPrefix))
            {
                var selfUrl = configuration["App:SelfUrl"];
                if (!string.IsNullOrEmpty(selfUrl))
                {
                    keyPrefix = selfUrl.Replace("http://", "")
                        .Replace("https://", "").Replace(":", "_");
                }
            }
            if (string.IsNullOrEmpty(keyPrefix))
                keyPrefix = "API";
            keyPrefix = $"{appName}_{keyPrefix}_";
            Configure<AbpDistributedCacheOptions>(options =>
            {
                options.KeyPrefix = keyPrefix;
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine($"CacheModule:{ex.Message}\r\n{ex.StackTrace}");
            if (ex.InnerException != null)
            {
                Console.WriteLine($"CacheModule:{ex.InnerException.Message}\r\n{ex.InnerException.StackTrace}");
            }
        }
    }



    /// <summary>
    /// 健康检查
    /// </summary>
    /// <param name="context"></param>
    private void ConfigureHealthChecks(ServiceConfigurationContext context)
    {
        context.Services.AddHealthChecks();
    }
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        //app.UseMiniProfiler();
        app.UseAbpRequestLocalization();
        app.UseCorrelationId();
        app.UseStaticFiles();
        app.UseSerilogRequestLogging();
        app.UseRouting();
        //自动返回数据格式化
        app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions
        {
            ShowIsErrorFlagForSuccessfulResponse = true,
            ExcludePaths = new AutoWrapperExcludePath[] {
                    // 严格匹配
                    new AutoWrapperExcludePath("/v1/api-docs", ExcludeMode.StartWith),
                }
        });

        app.UseCors();
        app.UseAuthentication();
        app.UseJwtTokenMiddleware();
        //app.UseIdentityServer();
        app.UseAuthorization();
        app.UseAuditing();
        app.UseAbpSerilogEnrichers();
        app.UseUnitOfWork();
        app.UseConfiguredEndpoints();
    }
}